﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Collections.ObjectModel;

namespace EnterpriseFramework.DataAccess
{
    public class Entities<T> : IList<T>, ICollection<T>, IEnumerable<T>
    {
        private List<T> list = new List<T>();
        Dictionary<int, BaseEntityChanges<T>> changesList = new Dictionary<int, BaseEntityChanges<T>>();
        List<T> NewList = new List<T>();
        List<T> deletedItem = new List<T>();

        internal List<T> AddedItem
        {
            get { return NewList; }
        }

        internal List<BaseEntityChanges<T>> UpdatedProperty
        {
            get { return changesList.Values.ToList<BaseEntityChanges<T>>(); }
        }

        internal List<T> DeletedItem
        {
            get { return deletedItem; }
        }

        internal bool ISNewList = false;

        public Entities()
        {
        }

        public Entities(List<T> AddList)
        {
            list.AddRange(AddList);
        }

        public T this[int index]
        {
            get
            {
                (this.list[index] as BaseEntity).PropertyChanged +=
                    new CPropertyChangedEventHandler(Entities_PropertyChanged);
                return (T)this.list[index];
            }
            set { this.list[index] = value; }
        }

        private void Entities_PropertyChanged(object sender, CPropertyChangedEventArgs e, int index)
        {
            if (changesList.ContainsKey(index))
            {
                if (!changesList[index].Property.ContainsKey(e.PropertyName))
                    changesList[index].Property.Add(e.PropertyName, e.PropertyValue);
                else
                    changesList[index].Property[e.PropertyName] = e.PropertyValue;
            }
            else
            {
                BaseEntityChanges<T> b = new BaseEntityChanges<T>();
                b.RowChange = (T)sender;

                b.Property.Add(e.PropertyName, e.PropertyValue);
                changesList.Add(index, b);
            }
        }

        /// <summary>
        /// Used to undo on any change on data.
        /// </summary>
        public void UndoUpdatedData()
        {
            foreach (int i in changesList.Keys)
                list[i] = changesList[i].RowChange;
            for (int i = 0; i < NewList.Count; i++)
                this.list.RemoveAt((NewList[i] as BaseEntity).RowIndex);
            for (int i = 0; i < deletedItem.Count; i++)
                this.list.Add(deletedItem[i]);
            changesList.Clear();
            NewList.Clear();
            deletedItem.Clear();
        }

        public void AddRange(Collection<T> collection)
        {
            for (int i = 0; i < collection.Count; i++)
            {
                if (collection[i] is BaseEntity)
                    (collection[i] as BaseEntity).RowIndex = this.list.Count;
                if (!ISNewList)
                    NewList.Add(collection[i]);
                this.list.Add(collection[i]);
            }
        }

        public void AddRange(T[] collection)
        {
            this.AddRange(collection);
        }

        public int IndexOf(T item)
        {
            return this.list.IndexOf(item);
        }

        public void Insert(int index, T item)
        {
            if (item is BaseEntity)
                (item as BaseEntity).RowIndex = index;
            if (!ISNewList)
                NewList.Add(item);
            this.list.Insert(index, item);
        }

        public void RemoveAt(int index)
        {
            deletedItem.Add(this.list[index]);
            this.list.RemoveAt(index);
        }

        public void Add(T item)
        {
            if (item is BaseEntity)
                (item as BaseEntity).RowIndex = this.list.Count;
            if (!ISNewList)
                NewList.Add(item);
            this.list.Add(item);
        }

        public void Clear()
        {
            this.list.Clear();
        }

        public bool Contains(T item)
        {
            return this.list.Contains(item);
        }

        public void CopyTo(T[] array, int arrayIndex)
        {
            this.list.CopyTo(array, arrayIndex);
        }

        public int Count
        {
            get { return list.Count; }
        }

        public bool IsReadOnly
        {
            get { throw new NotImplementedException(); }
        }

        public bool Remove(T item)
        {
            deletedItem.Add(item);
            return this.list.Remove(item);
        }

        public IEnumerator<T> GetEnumerator()
        {
            return list.GetEnumerator();
        }

        IEnumerator IEnumerable.GetEnumerator()
        {
            return list.GetEnumerator();
        }
    }

    /// <summary>
    /// Used to mention all chnage property name,value and original object(to support undo of this update)
    /// </summary>
    internal class BaseEntityChanges<T>
    {
        private Dictionary<string, object> property = new Dictionary<string, object>();
        public Dictionary<string, object> Property
        {
            get { return property; }
            set { property = value; }
        }

        public T RowChange { get; set; }
    }
}
